<?php

namespace App\Http\Controllers\Aliyun;

use App\Contracts\CacheKey;
use App\Http\Controllers\Controller;
use App\Models\Attachment;
use Hyn\Tenancy\Environment;
use Hyn\Tenancy\Models\Website;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Log;
use Symfony\Component\Console\Input\Input;

class OSSController extends Controller
{

    protected function getOSSBaseDirectory()
    {
        $tenancy = app(Environment::class);
        return $basePath = "/"
            . config("app.name")
            . "/"
            . $tenancy->hostname()->fqdn
            . "/attachments/";
    }

    public function policy()
    {
        $disk = Storage::disk("oss");

        $attachableType = request()->get('attachable_type');
        $modelName = str_replace("\\", "", $attachableType);
        $attachableTypeSnake = Str::snake($modelName);
        $attachableId = request()->get('attachable_id');
        $customData = [
            "upload_number" => Str::uuid(),
            "attachable_id" => $attachableId,
            "attachable_type" => $attachableType,
            "uploaded_by" => "" . auth()->user()->id
        ];
        $prefix = $attachableId;
        if (empty($attachableId)) {
            $prefix = "temporary";
        }

        $targetDirectory = $this->getOSSBaseDirectory() . $attachableTypeSnake . "/" . $prefix . "/";
        // 处理回调URL
        $callBackUrl = config('aliyun.oss_callback');
        if (Str::startsWith($callBackUrl, "/")) {
            $httpProtocol = Str::startsWith(config("app.url"), "https") ? "https://" : "http://";
            $callBackUrl = $httpProtocol . tenantDomain() . $callBackUrl;
            Log::info('$callBackUrl => ' . $callBackUrl);
        }
        // 生成签名
        $config = $disk->signatureConfig($targetDirectory, $callBackUrl, $customData, $expire = 60);
        $config = json_decode($config);
        $arr = ["config"];
        if (config("app.env") == "testing") {
            array_push($arr, ["targetDirectory", 'callBackUrl']);
        }
        return response()->json(compact($arr));
    }

    // callback from Aliyun OSS
    // 文件直传的阿里云回调接口
    /**
     * $data example
     * array (
     * 'filename' => 'tmp/abc/Famous Sayings for G2--FV .doc',
     * 'size' => '29696',
     * 'mimeType' => 'application/octet-stream',
     * 'height' => '',
     * 'width' => '',
     * 'a' => '',
     * )
     * @return \Illuminate\Http\JsonResponse
     */
    public function fileUploadedCallback()
    {
        $this->hackLocalDev();
        $verify = true;
        if (config("app.env") == "testing") {
            // for unit test
            $data = request()->only(["filename", "attachable_type", "attachable_id", "mimeType", "size", "uploaded_by", "upload_number"]);
        } else {
            $disk = Storage::disk("oss");
            list($verify, $data) = $disk->verify();
        }
        Log::info("OSS file upload callback ");
        Log::info($data);
        // create a attachment
        if (!$verify) {
            Log::warning("Attachment failed");
            Log::warning($data);
            return response()->json([]);
        }
        try {
            $attachment = $this->createAttachment($data);
            // create a cache key for front query
            $uploadNumber = $data['upload_number'] . "_";
            Log::info("save upload file status for 10min ". CacheKey::KEY_ATTACHMENT_PREFIX . $uploadNumber . $attachment->filename);
            Cache::put(CacheKey::KEY_ATTACHMENT_PREFIX . $uploadNumber . $attachment->filename, true, now()->addMinutes(10));
        } catch (\Exception $exception) {
            Log::warning($exception->getMessage());
            return response()->json([]);
        }
        $result = 'ok';
        return response()->json(compact(['result']));
    }

    /**
     * @return \Illuminate\Http\JsonResponse
     */
    public function checkUploadStatus()
    {
        $fileName = request()->get('file_name');
        $uploadNumber = request()->get('upload_number');
        Log::info("checkUploadStatus file with key " . CacheKey::KEY_ATTACHMENT_PREFIX . $uploadNumber . "_" . $fileName);
        $result = Cache::has(CacheKey::KEY_ATTACHMENT_PREFIX . $uploadNumber . "_" . $fileName);
        return response()->json(compact(['result']));
    }

    private function getFileName($path)
    {
        $arr = explode('/', $path);
        return end($arr);
    }

    private function hackLocalDev()
    {
        if (!config("app.debug")) {
            return;
        }
        $tenancy = app(Environment::class);
        $devWebsite = Website::where("uuid", config("tenancy.dev_app"))->first();
        if (empty($devWebsite)) {
            return;
        }
        $tenancy->tenant($devWebsite);
        Log::debug(' fileUploadedCallback hackLocalDev' . $tenancy->hostname());
    }

    /**
     * @param $modelId
     * @param $model
     * @param $fileName
     * @param array $data
     */
    private function createAttachment(array $data): Attachment
    {
        $fileName = $data['filename'];
        $model = $data['attachable_type'];
        $modelId = $data['attachable_id'];
        $isValid = $model::where("id", $modelId)->count(); // here to check if the attachment type and id is correct
        if ($isValid == 0) {
            throw new \Exception("资源不存在，无法上传 type [$model] and id [$modelId] ");
        }
        $attachment = new Attachment();
        $attachment->attachable_id = $modelId;
        $attachment->attachable_type = $model;
        $attachment->name = $this->getFileName($fileName);
        $attachment->filename = $fileName;
        $attachment->type = $data['mimeType'];
        $attachment->size = $data['size'];
        $attachment->upload_by = $data['uploaded_by'];
        $attachment->save();
        return $attachment;
    }
}
